﻿using System;
using System.Linq;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using System.IO;
using System.Diagnostics;

[LoadInSeparateAppDomainAttribute]
public class AppDomainWrapperTask : AppDomainIsolatedTask
{
	public AppDomainWrapperTask()
	{
		ParamNames = new ITaskItem[0];
		ArrayNames = new ITaskItem[0];
		OutArrayNames = new ITaskItem[0];
		OutParamNames = new ITaskItem[0];
	}

	[Required]
	public string Task { get; set; }

	[Required]
	public string Bin { get; set; }

	public ITaskItem[] ParamNames { get; set; }
	public ITaskItem[] OutParamNames { get; set; }
	public ITaskItem[] ArrayNames { get; set; }
	public ITaskItem[] OutArrayNames { get; set; }

	#region Parameters

	public string Param1 { get; set; }
	public string Param2 { get; set; }
	public string Param3 { get; set; }
	public string Param4 { get; set; }
	public string Param5 { get; set; }
	public string Param6 { get; set; }
	public string Param7 { get; set; }
	public string Param8 { get; set; }
	public string Param9 { get; set; }

	public ITaskItem[] Array1 { get; set; }
	public ITaskItem[] Array2 { get; set; }
	public ITaskItem[] Array3 { get; set; }
	public ITaskItem[] Array4 { get; set; }
	public ITaskItem[] Array5 { get; set; }
	public ITaskItem[] Array6 { get; set; }
	public ITaskItem[] Array7 { get; set; }
	public ITaskItem[] Array8 { get; set; }
	public ITaskItem[] Array9 { get; set; }

	[Output]
	public ITaskItem[] OutArray1 { get; set; }
	[Output]
	public ITaskItem[] OutArray2 { get; set; }
	[Output]
	public ITaskItem[] OutArray3 { get; set; }
	[Output]
	public ITaskItem[] OutArray4 { get; set; }
	[Output]
	public ITaskItem[] OutArray5 { get; set; }
	[Output]
	public ITaskItem[] OutArray6 { get; set; }
	[Output]
	public ITaskItem[] OutArray7 { get; set; }
	[Output]
	public ITaskItem[] OutArray8 { get; set; }
	[Output]
	public ITaskItem[] OutArray9 { get; set; }

	[Output]
	public string OutParam1 { get; set; }
	[Output]
	public string OutParam2 { get; set; }
	[Output]
	public string OutParam3 { get; set; }
	[Output]
	public string OutParam4 { get; set; }
	[Output]
	public string OutParam5 { get; set; }
	[Output]
	public string OutParam6 { get; set; }
	[Output]
	public string OutParam7 { get; set; }
	[Output]
	public string OutParam8 { get; set; }
	[Output]
	public string OutParam9 { get; set; }

	#endregion

	public override bool Execute()
	{
		Type taskType;
		try
		{
			AppDomain.CurrentDomain.AssemblyResolve += CurrentDomainAssemblyResolve;
			taskType = Type.GetType(Task);
		}
		finally
		{
			AppDomain.CurrentDomain.AssemblyResolve -= CurrentDomainAssemblyResolve;
		}

		if (taskType == null)
		{
			throw new Exception("Type '" + Task + "' does not found");
		}

		var task = (AppDomainIsolatedTask)Activator.CreateInstance(taskType);

		task.BuildEngine = BuildEngine;
		task.HostObject = HostObject;

		for (var i = 0; i < ParamNames.Length; i++)
		{
			var paramName = ParamNames[i];
			var value = GetType().GetProperty("Param" + (i + 1)).GetValue(this, null);
			taskType.GetProperty(paramName.ItemSpec).SetValue(task, value, null);
		}

		for (var i = 0; i < ArrayNames.Length; i++)
		{
			var arrayName = ArrayNames[i];
			var value = GetType().GetProperty("Array" + (i + 1)).GetValue(this, null);
			taskType.GetProperty(arrayName.ItemSpec).SetValue(task, value, null);
		}

		var result = task.Execute();

		for (var i = 0; i < OutParamNames.Length; i++)
		{
			var outParamName = OutParamNames[i];
			var value = taskType.GetProperty(outParamName.ItemSpec).GetValue(task, null);
			GetType().GetProperty("OutParam" + (i + 1)).SetValue(this, value, null);
		}

		for (var i = 0; i < OutArrayNames.Length; i++)
		{
			var outArrayName = OutArrayNames[i];
			var value = taskType.GetProperty(outArrayName.ItemSpec).GetValue(task, null);
			GetType().GetProperty("OutArray" + (i + 1)).SetValue(this, value, null);
		}

		return result;
	}

	Assembly CurrentDomainAssemblyResolve(object sender, ResolveEventArgs args)
	{
		var i = Task.IndexOf(',');
		if (i == -1)
		{
			throw new Exception("Specify task name with assembly, like 'MyTask, MyAssembly.dll'");
		}
		var file = Path.Combine(Bin, Task.Substring(i).Trim().Trim(',').Trim());
		var asm = Assembly.LoadFrom(file);
		Debug.Assert(asm != null);
		return asm;
	}
}